import logging
from datetime import datetime

from ldap3 import Server, Connection, ALL, SUBTREE
from ldap3.core.exceptions import LDAPBindError, LDAPSocketOpenError
from rest_framework import status
from rest_framework.response import Response
from rest_framework_jwt.settings import api_settings
from rest_framework_jwt.views import JSONWebTokenAPIView, ObtainJSONWebToken, RefreshJSONWebToken, VerifyJSONWebToken

from FasterRunner.settings import LDAP_LOGIN_SERVER, LDAP_LOGIN_BIND_DN, LDAP_LOGIN_SEARCH_STANDARD, LDAP_LOGIN_SEARCH_DN, LDAP_LOGIN_BIND_PASSWORD


def try_login_ldap(username, password):
    """ 使用账号密码登录
    步骤1：先连接ldap
    步骤2：使用username，通过bind账号查询username对应的dn
    步骤3：使用步骤2中的dn 与password，登录ldap进行验证
    """
    # 使用bind账号连接ldap server
    server = Server(LDAP_LOGIN_SERVER, get_info=ALL)
    try:
        with Connection(server, LDAP_LOGIN_BIND_DN, LDAP_LOGIN_BIND_PASSWORD) as conn:
            print("bind 连接：",conn.extend.standard.who_am_i())  # 测试bind能否正确连接
            # 通过username 拼接search_filter 搜索用户条目，取得dn值
            search_filter = "(" + str(LDAP_LOGIN_SEARCH_STANDARD).format(username) + ")"
            # print("search_filter:",search_filter)
            res = conn.search(LDAP_LOGIN_SEARCH_DN, search_filter, search_scope=SUBTREE)
            # print("res:",res)
            # 判断能否获得dn
            if not res:
                logging.error("Cannot find user %s" % username)
                return
            entry = conn.response[0]
            if 'dn' not in entry:
                logging.error("Invalid username or password")
                return

            # 使用dn值与密码进行登录验证
            # print("开始ldap登录验证...")
            try:
                conn = Connection(server, entry['dn'], password, auto_bind=True)
                if not conn:
                    logging.error("Password incorrect for user %s", username)
                    return
            except LDAPBindError:
                print("ldap登录验证失败！")
                return
            # print("ldap登录验证完成")
            return True
    except LDAPSocketOpenError:
        logging.error(f"ldap 连接失败 {LDAP_LOGIN_BIND_DN}, {LDAP_LOGIN_BIND_PASSWORD}")
        return False
    except Exception as e:
        logging.error(f"ldap 异常 {str(e)}")
        return False

def jwt_response_payload_handler(token, user=None, request=None):
    """
        自定义jwt认证成功返回数据
    """
    return {
        'code': 1001,
        'token': token,
        'id': user.id,
        'username': user.username,
        'is_superuser': user.is_superuser
    }


def jwt_response_payload_error_handler(serializer, request=None):
    """
        自定义jwt认证失败返回数据
    """
    return {
        "code": 1002,
        "msg": "用户名错误 | 密码错误 | ldap未授权",
        "detail": serializer.errors
    }


class MyJSONWebTokenAPIView(JSONWebTokenAPIView):

    def post(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        if serializer.is_valid():
            user = serializer.object.get('user') or request.user
            token = serializer.object.get('token')
            response_data = jwt_response_payload_handler(token, user, request)
            response = Response(response_data)
            if api_settings.JWT_AUTH_COOKIE:
                expiration = (datetime.utcnow() +
                              api_settings.JWT_EXPIRATION_DELTA)
                response.set_cookie(api_settings.JWT_AUTH_COOKIE,
                                    token,
                                    expires=expiration,
                                    httponly=True)
            return response
        error_data = jwt_response_payload_error_handler(serializer, request)
        return Response(error_data, status=status.HTTP_200_OK)


class MyObtainJSONWebToken(ObtainJSONWebToken, MyJSONWebTokenAPIView):
    pass


class MyRefreshJSONWebToken(RefreshJSONWebToken, MyJSONWebTokenAPIView):
    pass


class MyVerifyJSONWebToken(VerifyJSONWebToken, MyJSONWebTokenAPIView):
    pass


obtain_jwt_token = MyObtainJSONWebToken.as_view()
refresh_jwt_token = MyRefreshJSONWebToken.as_view()
verify_jwt_token = MyVerifyJSONWebToken.as_view()

if __name__ == '__main__':
    print(try_login_ldap("guanfuchang", "Milton@825@rf"))
